TAD's Quick ASP/Database tut 5

TAD

Input - domain of the fools

One of the common causes of bugs with ASP pages (and applications in general) is that of user input. You won't believe how many websites crash or have security holes simply because the coder was too lazy to perform some very, very simple checks before using some input data. A small validation routine at the start can prevent bugs and ugly 'fixes' much later on. An illegal character might only cause the ASP page to throw up a error and stop, but in some cases a SQL statement can be 'hacked' and used to perform another action. When you think about the SQL string being built up from SQL commands, delimiting characters (such as ' and %) and input data it isn't hard to see why input data validation is SO important. To test your own ASP code try using ' % and & in your input form-fields and see what happens.

Checking the input data can be done using some simple rules such as only allowing certain characters, checking the order of certain characters (eg. a '(' must come before a ')' in phone numbers), checking that certain characters only appear once (eg. the @ character in an email address), checking a string can be converted into a number or a currency. Another form of validation/verification is that the input lies within some range or if a certain ID number exists for example.

Simple Validation

Depending on how you are going to use the input data you need to write the validation accordingly. You will often find for registration/profile pages you will need many different 'types' of data, most of which will be variations on a Numeric string Or a Text string. Because most of the main validation routines are going to be identical I normally code a single validation pass it the the value to check, the 'input type' and an error message.

The rules for checking a valid email address are quite long, so I won't go into too much detail here but instead show you a flexible routine that should be your FIRST step in validation input data.

Include File - validation.asp
<%
Dim ValidateError
Function InsertError(theStr)
   If theStr="" Then
     InsertError = " "
   Else
     InsertError = theStr
   End If
End Function

Function Validate(theType, theValue, theOpt)
   Dim good, bad, validchars, ch, i, bracket, atsign
   good = False
   bad = False

 '// Is the value empty ? (check if optional) //
   If theValue="" And theOpt=False Then bad=True

 '// Determine type of data and set valid characters //
   Select Case Ucase(theType)
   Case "NAME"
     validchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ'-"
   Case "PHONE"
     validchars = "0123456789+ "
   Case "INTEGER"
     If Not isNumeric(theValue) Then bad=True
     validchars = "0123456789"
   Case "ADDRESS"
     validchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-'0123456789" & vbCRLF
   Case "NOTE"
     validchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-'0123456789:;()?!@#" & vbCRLF
   Case "EMAIL"
     validchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789@-"
   Case Else
     validchars = ""
     bad = True 
   End Select
   
 '// Check each char in input value //
   For i=1 To Len(theValue)
     ch = Ucase(Mid(theValue,i,1))
     If Instr(validchars,ch)<1 Then bad=True
     If ch="(" Then bracket=bracket+1
     If ch=")" Then bracket=bracket-1
     If bracket<0 or bracket>1 Then bad=True
     If ch="@" And Ucase(theType)="EMAIL" Then
       atsign=atsign+1
       if i<2 Or i>(Len(theValue)-2) Then bad=True
     End If
   Next

   If Ucase(theType)="EMAIL" And atsign<>1 Then bad=True

 '// pass back result flag //
   If bad=True Then
     ValidateError = True
     Validate = "<FONT COLOR=#FF0000>X</FONT>"
   Else
     Validate = ""
   End If
End Function
%>

Here is the long validate.asp example form:

[Editor's note: Since several lines exceeded the width of the article window, I've had to insert a line-break. However, the code won't run unless you remove the line-break. The lines concerned have therefore been marked with the string <!-- !!! -->. //Adok]

<%@ Language=VBScript %>
 <% Option Explicit %>
 <!-- #INCLUDE FILE="validation.asp" -->
<%
 Dim action
 Dim formTitle, formButton, formAction
 Dim firstname, lastname, age, email, address, phone
 Dim errFirstname, errLastname, errAge, errEmail, errAddress, errPhone 
 formTitle  = "Please enter your details"
 formButton = "Submit details"
 formAction = "validate.asp?action=validate"
   
 '// Do we need to validate the input data? //
 action = Request.QueryString("action")
 If action="validate" Then
   '// get input data from submitted form //
   firstname = Trim(Request.Form("firstname"))
   lastname = Trim(Request.Form("lastname"))
   age = Trim(Request.Form("age"))
   address = Trim(Request.Form("address"))
   email = Trim(Request.Form("email"))
   phone = Trim(Request.Form("phone"))
   
   '// validation all the input data //
   ValidateError = False
   errFirstname = Validate("NAME", firstname, False)
   errLastName = Validate("NAME", lastname, False)
   errAge = Validate("INTEGER", age, False)
   errAddress = Validate("ADDRESS", address, False)
   errEmail = Validate("EMAIL", email, False)
   errPhone = Validate("PHONE", phone, False)
   
   '// Are there any errors ? //
   If ValidateError Then
     formTitle = "Please correct the errors below"
     formButton = "Submit again"
     formAction = "validate.asp?action=validate"
   Else
     '// here we would add stuff to the database //
     '// but here we just redirect to thankyou page //
     Response.Redirect "thankyou.asp?f=" & Server.URLEncode(firstname)
       &"&l=" & Server.URLEncode(lastname) & "&e="
       & Server.URLEncode(email)
   End If
 End If%>
 <html>
 <head>
 <title>Example validation</title>
 </head>
 <body>
 <form name="form1" method="post" action="<%=formAction%>">
 <table width="80%" border="0" align="center" cellpadding="0"
           cellspacing="2">                                   <!-- !!! -->
 <tr align="center" bgcolor="#FFFFCC"> 
 <td colspan="3"><%=formTitle%></td>
 </tr>
 <tr> 
 <td width="16%" align="right">First name:</td>
 <td width="76%"><input name="firstname" type="text" id="firstname"
           value="<%=firstname%>" maxlength="50"></td>        <!-- !!! -->
 <td width="8%" align="center"><%=InsertError(errFirstname)%></td>
 </tr>
 <tr> 
 <td align="right">Last name:</td>
 <td><input name="lastname" type="text" id="lastname"
          value="<%=lastname%>" maxlength="50"></td>          <!-- !!! -->
 <td align="center"><%=InsertError(errLastname)%></td>
 </tr>
 <tr> 
 <td align="right">Age:</td>
 <td><input name="age" type="text" id="age"  value="<%=age%>"
          maxlength="3"></td>                                 <!-- !!! -->
 <td align="center"><%=InsertError(errAge)%></td>
 </tr>
 <tr> 
 <td align="right">Address:</td>
 <td><textarea name="Address" rows="5" id="Address"><%=address%>
          </textarea></td>                                    <!-- !!! -->
 <td align="center"><%=InsertError(errAddress)%></td>
 </tr>
 <tr> 
 <td align="right">email:</td>
 <td><input name="email" type="text" id="email"
          value="<%=email%>" maxlength="100"></td>            <!-- !!! -->
 <td align="center"><%=InsertError(errEmail)%></td>
 </tr>
 <tr> 
 <td align="right">Phone:</td>
 <td><input name="phone" type="text" id="phone"
          value="<%=phone%>" maxlength="20"></td>             <!-- !!! -->
 <td align="center"><%=InsertError(errPhone)%></td>
 </tr>
 <tr align="center" bgcolor="#FFFFCC"> 
 <td colspan="3"><input type="submit" name="Submit"
          value="<%=formButton%>"></td>                       <!-- !!! -->
 </tr>
 </table>
 </form>
 </body>
 </html> 

As you can hopefully work out from the above code, the form submits data to itself (the formAction URL). This way we can easily indicate any errors to the user by marking a red X on the form itself. If everything is okay the page is redirected to the thankyou.asp page. Of course in a real ASP page the input data would be stored in the database (after possibly checking for duplicate usernames, password, address, emails etc... - you can do this by using some SQL SELECT statements).

Summary

It should be very easy to extend the validation.asp functions to support more types of input data and add better validation rules as you need to. For certain types of input you will need to 'fix' certain control codes in order for them to be displayed correctly as HTML (such as replacing vbCRLF with <br> and dealing with < > & characters using the standard HTML < > and & encodings).

Another point worth thinking about is multiple blank lines and input data consisting of a long block of characters with no space between them (eg. 'a' repeated 400 times). In cases like these I suggest coding some 'formatting' functions and use them to take the input data and prepare it before outputing it using Response.Write or the shorter <%= ... %> method.

When inserting URL links you need to encode the string using the Server.URLEncode( ) function. This takes a string an converts the special characters (such as Space ' ') into those %nn escape-code sequences (eg. 'Hello%20World').

Depending on the type of message board/email system you are developing you may also want to consider a basic form of 'swear word filtering' and replace words like Fuck with F***. Of course no filter is fool proof, thats why many boards are moderated by real people. That's the main problem with allowing users to input stuff... they usually do things they really shouldn't do.

Until next time...

Next time we will look at the messages, navigation and putting it all together. But this will all have to wait until Hugi 28 due to having an extreme lack of free-time and energy :(

Happy Scriptin'

TAD